/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkSipTcpListener.h"
#include "MkSip/MkSipServer.h"
#include "MkSipTcpSession.h"
#include "MkSip/MkSipServer.h"
#include "MkSipTcpClient.h"
#include "MkEvent/MkEventPool.h"
#include "MkUtil/MkUtil.h"
#include "MkUtil/MkLog.h"

CMkSipTcpListener::CMkSipTcpListener(CMkEventPool* pEventPool, CMkTimer* pTimer, CMkSipServer* pParent)
    : CMkTcpServer(pEventPool, pTimer, 1000)
    , CMkSipListener(pParent)
{

}

Uint32 CMkSipTcpListener::Start(const MkString& ListenHost, Uint16 ListenPort, const MkString& PublicHost, Uint16 PublicPort)
{
    Uint32 ErrorCode = Create();
    MkCheckErrorLog(ErrorCode, "sip tcp server create socket failed\n");
    ErrorCode = Bind(ListenPort, ListenHost);
    MkCheckErrorLog(ErrorCode, "sip tcp server bind(%s:%d) failed\n", ListenHost.c_str(), ListenPort);

    ErrorCode = Listen();
    MkCheckErrorLog(ErrorCode, "tcp listener listen failed\n");

    return CMkSipListener::Start(ListenHost, ListenPort, PublicHost, PublicPort);
}

Uint32 CMkSipTcpListener::SendSipMessage(const CMkSipMessage& SipMessage, const SipMsgCompleteCallback& Cb/* = nullptr*/, BOOL bResend/* = FALSE*/)
{
    Uint32 ErrorCode = NoneError;
    CMkSocket * pSocket = nullptr;
    MkString strKey = SipMessage.GetRemoteHost() + std::to_string(SipMessage.GetRemotePort());
    m_TcpRouterLock.RLock();
    MkMap<MkString, CMkSocket*>::iterator itRemote = m_MapTcpRouter.find(strKey);
    MkMap<MkString, CMkSocket*>::iterator itContact = m_MapTcpRouter.end();
    if (!SipMessage.GetContactHost().empty()) {
        strKey = SipMessage.GetContactHost() + std::to_string(SipMessage.GetContactPort());
        itContact = m_MapTcpRouter.find(strKey);
    }
    if (itRemote != m_MapTcpRouter.end()) {
        pSocket = itRemote->second;
    } else if (itContact != m_MapTcpRouter.end()) {
        pSocket = itContact->second;
    }
    if (pSocket) {
        SocketSendSipMessage(pSocket, SipMessage, Cb, bResend);
    }
    m_TcpRouterLock.RUnlock();
    if (!pSocket) {
        CMkSipTcpClient *pClient = new CMkSipTcpClient(GetEventPool()->GetFreeEvent(), GetTimer(), [this](CMkTcpClient* pClient) {
            m_TcpRouterLock.WLock();
            MkMap<MkString, CMkSocket*>::iterator itRouter = m_MapTcpRouter.begin();
            for (; itRouter != m_MapTcpRouter.end(); itRouter++) {
                if (itRouter->second == pClient) {
                    m_MapTcpRouter.erase(itRouter);
                    break;
                }
            }
            m_TcpRouterLock.WUnlock();
            MkDelete(pClient);
        }, this);
        MkString ConnectHost = SipMessage.GetContactHost().empty() ? SipMessage.GetRemoteHost() : SipMessage.GetContactHost();
        Uint16 ConnectPort = SipMessage.GetContactHost().empty() ? SipMessage.GetRemotePort() : SipMessage.GetContactPort();
        ErrorCode = pClient->ConnectServer(ConnectHost, ConnectPort, [this, SipMessage, bResend, pClient, strKey, Cb]() {
            SocketSendSipMessage(pClient, SipMessage, Cb, bResend);
            m_TcpRouterLock.WLock();
            m_MapTcpRouter[strKey] = pClient;
            m_TcpRouterLock.WUnlock();
        });
    }
    return ErrorCode;
}

Uint32 CMkSipTcpListener::SocketSendSipMessage(CMkSocket* pSocket, const CMkSipMessage& SipMessage, const SipMsgCompleteCallback& Cb/* = nullptr*/, BOOL bResend/* = FALSE*/)
{
    CMkSipMessage SendMsg = SipMessage;
    if (SipMessage.IsRequest()
        && MkSipMethodAck != SipMessage.GetMethod()) {
        CMkSipVia Via;
        Via.SetProtocol("TCP");
        Via.UpdateParam("rport", "");
        Via.SetHost(pSocket->GetLocalHost());
        Via.SetPort(pSocket->GetLocalPort());
        if (bResend
            && !SipMessage.GetVecVia().empty()) {
            SendMsg.RemoveFirstVia();
            Via.SetBranch(SipMessage.GetVecVia().begin()->GetBranch());
        } else {
            Via.SetBranch("z9hG4bK" + std::to_string(CMkUtil::GetRandomNumber()));
        }
        SendMsg.AddVia(Via);
    }
    if (Cb) {
        Cb(SendMsg);
    }

    MkString strSipMsg;
    SendMsg.ToString(strSipMsg);
    if (CMkSipUtil::GetInstance().DebugMessageContent()) {
        MkDebugLog("send sip message:\n%s\n", strSipMsg.c_str());
    }
    return pSocket->Send((const Uint8*)strSipMsg.c_str(), strSipMsg.size());
}

Uint32 CMkSipTcpListener::OnSipRecved(const Uint8* pBuf, Uint32 nLen, const MkString& RemoteHost, Uint16 RemotePort, const MkString& LocalHost, Uint16 LocalPort, Uint32& nUsed)
{
    return GetParent()->OnSipBufferRecved(pBuf, nLen, RemoteHost, RemotePort, LocalHost, LocalPort, TRUE, nUsed);
}

CMkTcpSession* CMkSipTcpListener::CreateNewSession(CMkEvent *pEvent, CMkTimer* pTimer)
{
    return new CMkSipTcpSession(pEvent, pTimer, this);
}

Uint32 CMkSipTcpListener::OnSessionAccepted(CMkTcpSession *pSession)
{
    MkString strKey = pSession->GetRemoteHost() + std::to_string(pSession->GetRemotePort());
    m_TcpRouterLock.WLock();
    m_MapTcpRouter[strKey] = pSession;
    m_TcpRouterLock.WUnlock();
    return NoneError;
}

Uint32 CMkSipTcpListener::OnSessionEventRemoved(CMkTcpSession* pSession)
{
    m_TcpRouterLock.WLock();
    MkMap<MkString, CMkSocket*>::iterator it = m_MapTcpRouter.begin();
    for (; it != m_MapTcpRouter.end(); it++) {
        if (pSession == it->second) {
            m_MapTcpRouter.erase(it);
            break;
        }
    }
    m_TcpRouterLock.WUnlock();
    return CMkTcpServer::OnSessionEventRemoved(pSession);
}

CMkSipTcpListener::~CMkSipTcpListener()
{
    m_TcpRouterLock.WLock();
    MkMap<MkString, CMkSocket*>::iterator it = m_MapTcpRouter.begin();
    for (; it != m_MapTcpRouter.end(); it++) {
        //only delete sip tcp client sip tcp session will delete by tcp server
        if (TcpClient == it->second->SocketType()) {
            MkDelete(it->second);
        }
    }
    m_MapTcpRouter.clear();
    m_TcpRouterLock.WUnlock();
}